HDU 5324 Boring Class(LIS、二维分块)
题意:
$N\le 5\times 10^4,给定2个长度为N的序列,A_i,B_i$
$现要选出对于2个序列同样的子序列,假设下标为p_1\le p_2\le \dots\le p_m$
$满足A_{p_1}\ge A_{p_2}\dots\ge A_{p_m}, 且B_{p_1}\ge B_{p_2}\dots\ge B_{p_m}$
$求最长的这样的子序列,打印下标,多解输出字典序最小解$
分析:
$如果直接按照LIS的dp方程转移是n^2的,考虑优化一下转移$
$由于要最小字典序的,所以得倒着搞,因为状态是以i结尾的嘛$
$倒着搞的话,对于一个点(up_i, down_i)看成(x,y)来说$
$每次转移就是找左上角的点,即x要小,y要大的点$
$这个我们可以用二维分块来搞,以\sqrt n的代价来转移$
$cdq分治,树套树都可以$
$时间复杂度为O(n\sqrt n)或者O(nlog^2n)$
二维分块代码:
//
// Created by TaoSama on 2016-03-22
// Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
const int B = 250;
int n;
int x[N], y[N];
typedef pair<int, int> P;
P pre[B][B]; //x's prefix max
struct Node {
int first, second;
int id;
} X[N], Y[N];
void compress(int *x, int delta) {
vector<pair<int, int> > xs;
for(int i = 1; i <= n; ++i) xs.push_back({x[i], delta * i});
sort(xs.begin(), xs.end());
for(int i = 1; i <= n; ++i)
x[i] = lower_bound(xs.begin(), xs.end(), make_pair(x[i],
delta * i)) - xs.begin() + 1;
}
void getMax(P& x, P y) {
if(x.first < y.first) x = y;
else if(x.first == y.first && x.second > y.second) x = y;
}
void update(int x, int y, int v, int id) {
X[y] = {x, v, id}, Y[x] = {y, v, id};
for(int i = 0; i <= y / B; ++i) getMax(pre[x / B][i], {v, id});
}
pair<int, int> query(int x, int y) {
pair<int, int> ret = {0, 0};
int tx = x / B, ty = y / B;
for(int i = tx * B; i < x; ++i)
if(Y[i].first > y) getMax(ret, {Y[i].second, Y[i].id});
for(int i = y + 1; i < (ty + 1) * B; ++i)
if(X[i].first < x) getMax(ret, {X[i].second, X[i].id});
for(int i = 0; i < tx; ++i) getMax(ret, pre[i][ty + 1]);
return ret;
}
int prevv[N];
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
while(scanf("%d", &n) == 1) {
for(int i = 1; i <= n; ++i) scanf("%d", x + i);
for(int i = 1; i <= n; ++i) scanf("%d", y + i);
compress(x, -1);
compress(y, 1);
memset(pre, 0, sizeof pre);
memset(Y, 0xc0, sizeof Y);
memset(X, 0x3f, sizeof X);
P ans = {0, 0};
memset(prevv, 0, sizeof prevv);
for(int i = n; i >= 1; --i) {
P ret = query(x[i], y[i]);
prevv[i] = ret.second;
update(x[i], y[i], ret.first + 1, i);
getMax(ans, {ret.first + 1, i});
}
printf("%d\n", ans.first);
vector<int> path;
int u = ans.second;
for(int i = 1; i <= ans.first; ++i) {
path.push_back(u);
u = prevv[u];
}
for(int i = 0; i < path.size(); ++i)
printf("%d%c", path[i], " \n"[i == path.size() - 1]);
}
return 0;
}